#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gevent import monkey
monkey.patch_socket()
from gevent import Greenlet
import Singleton
import ThreadSafeSingletion
import ConfigFileData
import SocketTools
import logging
import socket
import time
from pageType import *
import trade_db_model_pb2
import stock_trade_pb2
import base_pb2

LOG_CURR_TIME = time.clock()
LOG_NEXT_TIME = 0

@Singleton.singleton
class Connection(Greenlet):
    def __init__(self):
        Greenlet.__init__(self)
        self.signal = {
            u'posit': None,
            u'money': None,
            u'trade': None,
            u'order': None
        }
        self.queue = ThreadSafeSingletion.SafeQueue()
        self.requesetId = 0
        self.config = ConfigFileData.Data()
        self.packTools = SocketTools.StructProtocol()
        self.RebuildConnection(self.config.get(u'StockServer.ip'), int(self.config.get(u'StockServer.port')))
        self.posit_signal = dict()
        self.order_signal = dict()
        self.Unitest_callback = dict()
        self.policyID_trades = dict()
        global LOG_NEXT_TIME
        LOG_NEXT_TIME = int(self.config.get(u"Start.LogTimeInterval")) + int(LOG_CURR_TIME)

    def RebuildConnection(self, ip, port):
        while True:
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.settimeout(5)
                self.sock.connect((ip, port))

            except:
                logging.warning(u"StockServerClient连接失败, 等待5秒后重新尝试...")
                time.sleep(5)
                continue
            break
        self.init_stock_server_data()

    def init_stock_server_data(self):
        self.send(base_pb2.CMD_QUERY_ASSET_REQ)
        msg = stock_trade_pb2.QueryPositionRequest()
        msg.code = ''
        self.send(base_pb2.CMD_QUERY_STOCK_POSITION_REQ, msg)
        msg = stock_trade_pb2.QueryOrderRequest()
        msg.order_no = ''
        self.send(base_pb2.CMD_QUERY_STOCK_ORDER_REQ, msg)
        msg = stock_trade_pb2.QueryStockKnockRequest()
        msg.order_no = ''
        self.send(base_pb2.CMD_QUERY_STOCK_KNOCK_REQ, msg)
        msg = stock_trade_pb2.SubTradeMsgRequest()
        msg.type = base_pb2.SUB_STOCK_ORDER
        self.send(base_pb2.CMD_SUBSCRIBE_TRADE_REQ, msg)
        msg = stock_trade_pb2.SubTradeMsgRequest()
        msg.type = base_pb2.SUB_STOCK_KNOCK
        self.send(base_pb2.CMD_SUBSCRIBE_TRADE_REQ, msg)
        msg = stock_trade_pb2.SubTradeMsgRequest()
        msg.type = base_pb2.SUB_ASSET
        self.send(base_pb2.CMD_SUBSCRIBE_TRADE_REQ, msg)
        msg = stock_trade_pb2.SubTradeMsgRequest()
        msg.type = base_pb2.SUB_STOCK_POSITION
        self.send(base_pb2.CMD_SUBSCRIBE_TRADE_REQ, msg)

    def send(self, package_type, message=""):
        if message != "":
            message = message.SerializeToString()
        requestID = self.RequesetId()
        data = self.packTools.pack([len(message), base_pb2.SYS_STOCK, package_type, requestID], message)
        self.sock.sendall(data)
        return requestID

    def Recv(self, Packlen):
        ret = str()
        while 1:
            recvBuf = self.sock.recv(Packlen-len(ret))
            if len(recvBuf) == Packlen-len(ret):
                ret += recvBuf
                break
            ret += recvBuf
        return ret

    def RequesetId(self):
        self.requesetId += 1
        return str(self.requesetId)

    def MoneySub(self, **kwargs):
        print 'MoneySub'
        message = kwargs['message']['body']
        rep = trade_db_model_pb2.StockAsset()
        rep.ParseFromString(message)
        self.queue.put([u'SS_Money_sub', rep, self.packTools.get_requestID(kwargs['message']['head'][3])])
        if self.signal[u'money']:
            self.signal[u'money']()

    def Money(self, **kwargs):
        message = kwargs['message']['body']
        rep = stock_trade_pb2.QueryAssetResponse()
        rep.ParseFromString(message)
        self.queue.put([u'SS_Money', rep, self.packTools.get_requestID(kwargs['message']['head'][3])])
        if self.signal[u'money']:
            self.signal[u'money']()

    def Trade(self, **kwargs):
        print 'TradeSub'
        message = kwargs['message']['body']
        rep = stock_trade_pb2.QueryStockKnockResponse()
        rep.ParseFromString(message)

        for i in rep.stock_knock:
            if self.policyID_trades.get(i.policy_id, None) is None:
                self.policyID_trades[i.policy_id] = list()
            self.policyID_trades[i.policy_id].append(i)
        if self.signal[u'trade']:
            self.signal[u'trade'](trades=rep.stock_knock)

        self.queue.put([u'SS_Trade', rep, self.packTools.get_requestID(kwargs['message']['head'][3])])

    def get_policyID_trade(self, policy_id):
        return self.policyID_trades.get(policy_id, [])

    def Order(self, **kwargs):
        print 'OrderSub'
        message = kwargs['message']['body']
        rep = stock_trade_pb2.QueryStockOrderResponse()
        rep.ParseFromString(message)
        self.queue.put([u'SS_Order', rep, self.packTools.get_requestID(kwargs['message']['head'][3])])
        if self.signal[u'order']:
            self.signal[u'order']()
        for i in rep.stock_order:
            func = self.order_signal.get(i.stock_id, None)
            if func:
                for j in func:
                    func[j](position=i)

    def Heart(self, **kwargs):
        data = self.packTools.pack([0, base_pb2.SYS_STOCK, base_pb2.CMD_HEARTBEAT_RESP, self.RequesetId()], '')
        self.sock.sendall(data)

    def Posit(self, **kwargs):
        print 'PositSub'
        message = kwargs['message']['body']
        rep = stock_trade_pb2.QueryPositionResponse()
        rep.ParseFromString(message)
        self.queue.put([u'SS_Posit', rep, self.packTools.get_requestID(kwargs['message']['head'][3])])
        if self.signal[u'posit']:
            self.signal[u'posit']()
        for i in rep.stock_position:
            func = self.posit_signal.get(i.stock_id, None)
            if func:
                for j in func:
                    func[j](position=i)

    def Error(self, **kwargs):
        message = kwargs['message']['body']
        rep = stock_trade_pb2.ErrResp()
        rep.ParseFromString(message)
        logging.warning("StockServerClient-Response-Error:{0}".format(rep.ret_message))

    def Sub(self, **kwargs):
        pass

    def add_posit_signal(self, code, speID, signalFunc):
        if self.posit_signal.get(code, None) is None:
            self.posit_signal[code] = dict()
        self.posit_signal[code][speID] = signalFunc

    def del_posit_signal(self, code, speID):
        try:
            self.posit_signal[code].pop(speID)
        except:
            pass

    def add_order_signal(self, code, speID, signalFunc):
        if self.order_signal.get(code, None) is None:
            self.order_signal[code] = dict()
        self.order_signal[code][speID] = signalFunc

    def del_order_signal(self, code, speID):
        try:
            self.order_signal[code].pop(speID)
        except:
            pass

    def OpenSignal(self, posit, money, trade, order):
        self.signal = {
            u'posit': posit,
            u'money': money,
            u'trade': trade,
            u'order': order
        }

    def CloseSignal(self):
        self.signal = {
            u'posit': None,
            u'money': None,
            u'trade': None,
            u'order': None
        }

    def unitest_SingleOrderResp(self, **kwargs):
        message = kwargs['message']['body']
        rep = stock_trade_pb2.SingleOrderResp()
        rep.ParseFromString(message)
        print 'unitest_SingleOrderResp:'

    def _run(self):
        global LOG_NEXT_TIME
        global LOG_CURR_TIME
        print '[Start]StockServer'
        switch = {
            base_pb2.CMD_SINGLE_ORDER_RESP: self.unitest_SingleOrderResp,
            base_pb2.CMD_QUERY_ASSET_RESP: self.Money,
            base_pb2.CMD_QUERY_STOCK_KNOCK_RESP: self.Trade,
            base_pb2.CMD_QUERY_STOCK_ORDER_RESP: self.Order,
            base_pb2.CMD_QUERY_STOCK_POSITION_RESP: self.Posit,
            base_pb2.CMD_SUBSCRIBE_TRADE_RESP: self.Sub,
            base_pb2.CMD_SUB_ASSET_REQ: self.MoneySub,
            base_pb2.CMD_SUB_STOCK_KNOCK_REQ: self.Trade,
            base_pb2.CMD_SUB_STOCK_ORDER_REQ: self.Order,
            base_pb2.CMD_SUB_STOCK_POSITION_REQ: self.Posit,
            0x8999: self.Error,
            base_pb2.CMD_HEARTBEAT_REQ: self.Heart,
        }
        while True:
            LOG_CURR_TIME = int(time.clock())
            if int(LOG_CURR_TIME) > int(LOG_NEXT_TIME):
                LOG_NEXT_TIME = int(self.config.get(u"Start.LogTimeInterval")) + int(LOG_CURR_TIME)
                # self.Heart()

            try:
                header = self.Recv(self.packTools.getHeaderLen())
                body = self.Recv(self.packTools.GetBodyLen(header))
                self.packTools.putData(header+body)
            except socket.error, e:
                if e.errno:
                    logging.warning(u"StockServerClient.recv 发生错误:{0}, 重新建立连接...".format(e))
                    self.RebuildConnection(self.config.get(u'StockServer.ip'), int(self.config.get(u'StockServer.port')))
                continue
            except:
                logging.warning(u"StockServerClient:_run:发生未知错误。")
                exit(-1)

            while 1:
                data = self.packTools.getOnce()
                if data:
                    pType = data['head'][2]
                    try:
                        switch[int(pType)](message=data)
                    except:
                        logging.warning(u"SSC处理行情包子类型：{0} 发生错误。".format(pType))
                else:
                    break